Skip to content

feat: add expo plugin and trusted origins #597

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: v3-main
Choose a base branch
from

Conversation

omar-bear
Copy link
Contributor

@omar-bear omar-bear commented Jul 2, 2025

Describe your changes

Add expo plugin and trusted origins to better-auth config, to make socials login works on start ui native using start ui web server

Summary by CodeRabbit

  • New Features

    • Added Expo platform support for authentication.
    • Introduced trusted origin(s) to improve authentication for native apps.
  • Chores

    • Added the Expo auth dependency.
    • Exposed a new AUTH_TRUSTED_ORIGIN configuration variable and updated environment examples.

Copy link

vercel bot commented Jul 2, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
start-ui ❌ Failed (Inspect) Jul 3, 2025 3:26pm
start-ui-web-restart ❌ Failed (Inspect) Jul 3, 2025 3:26pm

Copy link
Contributor

coderabbitai bot commented Jul 2, 2025

Walkthrough

Adds the @better-auth/expo plugin to the authentication configuration, introduces a trustedOrigins setting for the auth config, adds the dependency, and exposes a new AUTH_TRUSTED_ORIGIN environment variable in the server env schema and example.

Changes

Cohort / File(s) Change Summary
Auth configuration
app/server/auth.tsx
Added expo() to the plugins array and added trustedOrigins: string[] to the exported betterAuth/auth configuration.
Environment schema & example
app/env/server.ts, /.env.example
Added AUTH_TRUSTED_ORIGIN to the server environment schema (z.string()) and added AUTH_TRUSTED_ORIGIN="start-ui-native://*" to the example env.
Dependencies
package.json
Added dependency "@better-auth/expo": "1.2.10" to dependencies.

Sequence Diagram(s)

sequenceDiagram
    participant ExpoApp as Client (Expo App)
    participant Server as Server (app)
    participant BetterAuth as BetterAuth core
    participant ExpoPlugin as @better-auth/expo

    ExpoApp->>Server: Start auth flow / callback
    Server->>BetterAuth: Invoke auth processing (plugins enabled)
    BetterAuth->>ExpoPlugin: Delegate Expo-specific handling
    ExpoPlugin-->>BetterAuth: Return plugin result
    BetterAuth-->>Server: Return auth result (validated against trustedOrigins)
    Server-->>ExpoApp: Complete auth response
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-add-expo-better-auth

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c8b459c and 123ca57.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • app/server/auth.tsx (3 hunks)
  • package.json (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
app/server/auth.tsx (1)
Learnt from: ivan-dalmet
PR: BearStudio/start-ui-web#532
File: src/server/config/oauth/providers/discord.ts:11-11
Timestamp: 2024-10-14T15:29:53.279Z
Learning: In `src/server/config/oauth/providers/discord.ts`, when defining the `zDiscordUser` schema, keep fields like `username` as nullable (e.g., `.nullish()`), as the Discord API might return null values despite the documentation stating otherwise.
🧬 Code Graph Analysis (1)
app/server/auth.tsx (1)
app/env/server.ts (1)
  • envServer (9-33)
🪛 ESLint
app/server/auth.tsx

[error] 21-21: 'inferAdditionalFields' is defined but never used. Allowed unused vars must match /^_/u.

(@typescript-eslint/no-unused-vars)


[error] 21-21: Remove this unused import of 'inferAdditionalFields'.

(sonarjs/unused-import)

🔇 Additional comments (4)
package.json (1)

47-47: LGTM - Dependency addition looks correct.

The @better-auth/expo dependency is properly added and the version aligns well with the existing better-auth ecosystem in the project.

app/server/auth.tsx (3)

5-5: LGTM - Expo plugin import correctly added.

The import is properly used in the plugins array configuration.


29-29: LGTM - Trusted origins configuration is appropriate.

The trusted origins configuration with start-ui-native:// and start-ui-native://* properly allows the native Expo app to authenticate with the web server. This follows the expected pattern for Expo deep linking authentication flows.


54-54: LGTM - Expo plugin correctly integrated.

The expo() plugin is properly added to the plugins array and will enable Expo-specific authentication functionality.

@@ -17,13 +18,15 @@ import { permissions } from '@/features/auth/permissions';
import { db } from '@/server/db';
import { sendEmail } from '@/server/email';
import { getUserLanguage } from '@/server/utils';
import { inferAdditionalFields } from 'better-auth/client/plugins';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove unused import.

The inferAdditionalFields import is not used anywhere in the code and should be removed to keep the codebase clean.

-import { inferAdditionalFields } from 'better-auth/client/plugins';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { inferAdditionalFields } from 'better-auth/client/plugins';
🧰 Tools
🪛 ESLint

[error] 21-21: 'inferAdditionalFields' is defined but never used. Allowed unused vars must match /^_/u.

(@typescript-eslint/no-unused-vars)


[error] 21-21: Remove this unused import of 'inferAdditionalFields'.

(sonarjs/unused-import)

🤖 Prompt for AI Agents
In app/server/auth.tsx at line 21, the import statement for
inferAdditionalFields from better-auth/client/plugins is unused. Remove this
import line entirely to clean up the code and avoid unnecessary imports.

Copy link

sonarqubecloud bot commented Jul 3, 2025

@ivan-dalmet ivan-dalmet added the v3 label Jul 11, 2025
@ivan-dalmet ivan-dalmet force-pushed the v3-main branch 2 times, most recently from cf02ee7 to cbaf95e Compare July 21, 2025 10:39
Copy link

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8776fa0 and fd9c460.

📒 Files selected for processing (2)
  • .env.example (1 hunks)
  • app/env/server.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Playwright E2E Tests

@@ -23,6 +23,7 @@ DATABASE_URL="postgres://${DOCKER_DATABASE_USERNAME}:${DOCKER_DATABASE_PASSWORD}
BETTER_AUTH_SECRET="REPLACE ME" # You can use `npx @better-auth/cli@latest secret` to a generated secret
SESSION_EXPIRATION_IN_SECONDS=2592000 # 30 days
SESSION_UPDATE_AGE_IN_SECONDS=86400 # 1 day (every 1 day the session expiration is updated)
AUTH_TRUSTED_ORIGIN="start-ui-native://*" # Mobile app scheme for trustedOrigins config
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Align env var with array semantics and allow multiple trusted origins (CSV).

better-auth expects an array for trustedOrigins. A single AUTH_TRUSTED_ORIGIN is limiting and semantically singular. Consider switching to a plural, comma-separated env to support multiple entries out of the box.

Apply this diff to the example:

-AUTH_TRUSTED_ORIGIN="start-ui-native://*" # Mobile app scheme for trustedOrigins config
+AUTH_TRUSTED_ORIGINS="start-ui-native://,start-ui-native://*" # Mobile app schemes for trustedOrigins config (CSV)

Follow-up:

  • Keep backward compatibility by reading both AUTH_TRUSTED_ORIGINS (CSV) and legacy AUTH_TRUSTED_ORIGIN (single), if you adopt the change in the code.
  • Ensure app/server/auth.tsx parses CSV when building the trustedOrigins array.

Run to locate all usages and assess impact (rename vs. dual support):

Additionally, verify whether better-auth accepts the wildcard form for custom schemes or if plain "scheme://" is sufficient:


🏁 Script executed:

#!/bin/bash
# Find occurrences of the old and proposed env names and trustedOrigins usage
rg -n -A2 -B2 $'AUTH_TRUSTED_ORIGIN\\b|AUTH_TRUSTED_ORIGINS\\b|trustedOrigins\\s*:'

Length of output: 756


🌐 Web query:

What formats does better-auth accept for the `trustedOrigins` option, and are custom URL schemes (e.g., start-ui-native:// or start-ui-native://*) supported?

💡 Result:

Better Auth accepts the following formats for the trustedOrigins option:

  • An array of static origin strings (e.g., "https://example.com")
  • A function that returns a list of origins dynamically
  • Wildcard patterns (including domain wildcards and protocol wildcards)
  • Custom URL schemes such as start-ui-native:// for deep links and mobile authentication flows[1][3][4].

Custom URL Schemes Support:

  • Trusted origins can use custom schemes like start-ui-native:// to enable deep linking for mobile or desktop apps[3][4].
  • You can list your custom scheme directly (e.g., "start-ui-native://").
  • Wildcards with custom schemes are supported when you have varying paths or want to match all routes within the scheme (e.g., "start-ui-native://*")[3].
  • This pattern is especially useful for Expo, React Native, and similar apps leveraging deep links after authentication[3].

Illustrative Example:

trustedOrigins: [
  "https://yourdomain.com",
  "start-ui-native://",
  "start-ui-native://*",
  "myapp-extension://*"
]
  • "start-ui-native://" allows exact matches.
  • "start-ui-native://*" matches any path or resource following the custom scheme, which is necessary if your app handles multiple deep-linked routes[3].

Key Details:

  • Wildcards can be used after both standard protocols and custom schemes, but the scheme itself (e.g., start-ui-native) must match exactly—wildcarding does not change the protocol/scheme matching logic[4].
  • For web origins, both domain and protocol wildcards are also supported (e.g., "https://*.example.com", "*.example.com"), with HTTPS preferred for security[4][1].
  • Custom schemes are intended for apps and browser extensions; for browsers, redirection to custom schemes usually must be handled client-side for security reasons[8].

References:

  • Full documentation: Better Auth config reference[1], security and trusted origins explanation[4], and Expo integration docs highlighting custom schemes and wildcards[3].
  • Client-side handling for redirects with custom schemes: GitHub discussion[8].

In summary:
Better Auth’s trustedOrigins can accept static URLs, dynamic lists, wildcard patterns, and custom URL schemes (including wildcards such as start-ui-native://*). Custom URL schemes are a first-class feature for deep linking in mobile and desktop apps and should be explicitly listed in your configuration for safe origin handling[3][4].

Citations:


Align AUTH_TRUSTED_ORIGINS for multiple origins

To support an array of trusted origins while preserving backward-compatibility:

• .env.example

-AUTH_TRUSTED_ORIGIN="start-ui-native://*" # Mobile app scheme for trustedOrigins config
+AUTH_TRUSTED_ORIGINS="start-ui-native://,start-ui-native://*" # Mobile app schemes for trustedOrigins config (CSV)

• app/env/server.ts
– Replace AUTH_TRUSTED_ORIGIN: z.string()
– Add AUTH_TRUSTED_ORIGINS: z.string().default('').transform(s => s.split(',').filter(Boolean))
– In your server config, coalesce:

const rawOrigins = env.AUTH_TRUSTED_ORIGINS.length
  ? env.AUTH_TRUSTED_ORIGINS
  : [env.AUTH_TRUSTED_ORIGIN];

• app/server/auth.tsx
– Change

trustedOrigins: [envServer.AUTH_TRUSTED_ORIGIN],

to

trustedOrigins: rawOrigins,

Ensure you read the new AUTH_TRUSTED_ORIGINS CSV first and fall back to the legacy AUTH_TRUSTED_ORIGIN for a smooth migration.

🤖 Prompt for AI Agents
In .env.example around line 26, the current AUTH_TRUSTED_ORIGIN entry only
supports a single origin; update the app to accept multiple origins by adding a
CSV-based AUTH_TRUSTED_ORIGINS variable and adjusting server parsing and usage:
in app/env/server.ts replace the single z.string() validator with a
z.string().default('').transform to split on commas and filter empties, and
coalesce at runtime to use AUTH_TRUSTED_ORIGINS when present or fall back to the
legacy AUTH_TRUSTED_ORIGIN; then update app/server/auth.tsx to pass the computed
rawOrigins to trustedOrigins instead of the single envServer.AUTH_TRUSTED_ORIGIN
so both multi-origin CSV and the old single value remain supported during
migration.

@@ -12,6 +12,7 @@ export const envServer = createEnv({
BETTER_AUTH_SECRET: z.string(),
SESSION_EXPIRATION_IN_SECONDS: z.coerce.number().int().default(2592000), // 30 days by default
SESSION_UPDATE_AGE_IN_SECONDS: z.coerce.number().int().default(86400), // 1 day by default
AUTH_TRUSTED_ORIGIN: z.string(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Provide a sane default and reduce friction in local dev.

AUTH_TRUSTED_ORIGIN is required with no default, while .env.example suggests "start-ui-native://*". Add a default to keep parity and avoid failures when the env is not set.

Apply this diff:

-    AUTH_TRUSTED_ORIGIN: z.string(),
+    AUTH_TRUSTED_ORIGIN: z.string().default('start-ui-native://*'),

Optional (future-proofing): if you decide to support multiple origins via CSV as suggested in .env.example, consider this alternative and adjust downstream code to expect an array:

// Optionally add alongside AUTH_TRUSTED_ORIGIN for backwards compatibility:
AUTH_TRUSTED_ORIGINS: z
  .string()
  .default('start-ui-native://,start-ui-native://*')
  .transform((v) =>
    v
      .split(',')
      .map((s) => s.trim())
      .filter(Boolean),
  ),

Confirm that app/server/auth.tsx actually consumes envServer.AUTH_TRUSTED_ORIGIN (or the proposed AUTH_TRUSTED_ORIGINS) when building the betterAuth trustedOrigins array, and that you’re not unintentionally hardcoding values:


🏁 Script executed:

#!/bin/bash
# Check usage of trustedOrigins and whether the env var is wired through
rg -n -A5 -B5 $'trustedOrigins\\s*:|AUTH_TRUSTED_ORIGIN\\b|AUTH_TRUSTED_ORIGINS\\b' app

Length of output: 1339


Provide a sane default for AUTH_TRUSTED_ORIGIN in your env schema

To match your .env.example and avoid runtime failures when AUTH_TRUSTED_ORIGIN isn’t set, add a default:

• File: app/env/server.ts (line 15)

-    AUTH_TRUSTED_ORIGIN: z.string(),
+    AUTH_TRUSTED_ORIGIN: z.string().default('start-ui-native://*'),

Optional future-proofing (support multiple origins via CSV):

AUTH_TRUSTED_ORIGINS: z
  .string()
  .default('start-ui-native://,start-ui-native://*')
  .transform(v =>
    v
      .split(',')
      .map(s => s.trim())
      .filter(Boolean),
  )

Note: app/server/auth.tsx already uses envServer.AUTH_TRUSTED_ORIGIN in its trustedOrigins array, so this default will be picked up automatically.

🤖 Prompt for AI Agents
In app/env/server.ts around line 15, the env schema requires AUTH_TRUSTED_ORIGIN
but provides no default which can cause runtime failures when the variable is
not set; update the zod schema to supply a sensible default (matching
.env.example) — e.g. set AUTH_TRUSTED_ORIGIN to a default like
'start-ui-native://,start-ui-native://*' or alternatively add
AUTH_TRUSTED_ORIGINS as a CSV string with a transform to produce an array of
trimmed, non-empty origins — ensure the schema uses .default(...) and, if using
the CSV approach, .transform(...) to split, trim and filter values so
app/server/auth.tsx can safely read trusted origins without runtime errors.

@@ -24,6 +25,7 @@ export const auth = betterAuth({
expiresIn: envServer.SESSION_EXPIRATION_IN_SECONDS,
updateAge: envServer.SESSION_UPDATE_AGE_IN_SECONDS,
},
trustedOrigins: [envServer.AUTH_TRUSTED_ORIGIN],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment to explain the purpose of this line would be great so people not using Start UI [native] will know that they can remove it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants